﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace IlianaRuler.CPL
{
    /// <summary>
    /// 命令类型
    /// </summary>
    public enum CommandType : byte
    {
        /// <summary>
        /// 登录
        /// </summary>
        Login                   = 0x01,
        /// <summary>
        /// 登出
        /// </summary>
        Logout                  = 0x02,
        /// <summary>
        /// 注册节点
        /// </summary>
        RegisterNode            = 0x03,
        /// <summary>
        /// 更新节点
        /// </summary>
        UpdateNode              = 0x04,
        /// <summary>
        /// 删除节点
        /// </summary>
        RemoveNode              = 0x05,
        /// <summary>
        /// 校时
        /// </summary>
        Timing                  = 0x06,
        /// <summary>
        /// 设置策略
        /// </summary>
        SetTactics              = 0x07,
        /// <summary>
        /// 控制状态
        /// </summary>
        Control                 = 0x08,
        /// <summary>
        /// 获取状态
        /// </summary>
        GetStatus               = 0x09,
        /// <summary>
        /// 自检
        /// </summary>
        SelfChecking            = 0x0a,
        /// <summary>
        /// 无操作(心跳)
        /// </summary>
        Noop                    = 0xff
    }

    /// <summary>
    /// 通讯层指令执行错误代码
    /// </summary>
    public enum ErrorCode : ushort
    {
        /// <summary>
        /// 执行成功
        /// </summary>
        Success                 = 0x0000,
        /// <summary>
        /// 通讯失败
        /// </summary>
        SocketFailed            = 0x0001,
        /// <summary>
        /// 无效的SmartServer
        /// </summary>
        InvalidServer           = 0x0002,
        /// <summary>
        /// 写入失败
        /// </summary>
        WriteFailed             = 0x0003,
        /// <summary>
        /// 无效的节点(别名)
        /// </summary>
        InvalidNode             = 0x3001,
        /// <summary>
        /// 节点NeruonID已被使用
        /// </summary>
        UsedNeruonID            = 0x3002,
        /// <summary>
        /// 绑定告警服务器失败
        /// </summary>
        BindAlertServerFailed   = 0x4001,
        /// <summary>
        /// 重复注册的节点(别名)
        /// </summary>
        ReduplicateNode         = 0x4002,
        /// <summary>
        /// 无效的控制指令
        /// </summary>
        InvalidCommand          = 0x2001,
        /// <summary>
        /// 数据采集失败
        /// </summary>
        CollectDataFailed       = 0x1301,
        /// <summary>
        /// 未连接或连接丢失
        /// </summary>
        Disconnected            = 0x9001,
        /// <summary>
        /// 未知错误
        /// </summary>
        Unknown                 = 0x0099
    }

    /// <summary>
    /// 校时类型
    /// </summary>
    public enum TimingType : byte
    {
        /// <summary>
        /// 指定SmartServer及其它所有节点
        /// </summary>
        Specified               = 0x01,
        /// <summary>
        /// 指定的节点
        /// </summary>
        Node                    = 0x02,
        /// <summary>
        /// 全部SmartServer及节点
        /// </summary>
        All                     = 0x03
    }

    /// <summary>
    /// 自检类型
    /// </summary>
    public enum SelfCheckingType : byte
    {
        /// <summary>
        /// 检测SmartServer
        /// </summary>
        Server                  = 0x01,
        /// <summary>
        /// 检测节点
        /// </summary>
        Node                    = 0x02
    }

    /// <summary>
    /// 策略规则类
    /// </summary>
    public class TacticRule
    {
        /// <summary>
        /// 触发时间
        /// </summary>
        public DateTime Time
        {
            get; set;
        }

        /// <summary>
        /// 设置状态
        /// </summary>
        public Single State
        {
            get; set;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <remarks>
    /// 协议基本格式：
    ///     ID(16 bytes)|CommandType(1 byte)|Length(4 bytes)|Content
    ///     指令流水     指令类型            协议包的总长度  包体    
    ///     －－检验码是对指令字和用户ID的MD5数据
    ///     
    ///     请求和应答均采用上述格式。
    /// </remarks>
    abstract class BaseProtocol
    {
        Guid _id;
        CommandType _type;

        protected BaseProtocol(CommandType type)
        {
            _id = Guid.NewGuid();
            _type = type;
        }

        protected abstract void WriteRequest(BinaryWriter streamWriter);
        protected abstract void ReadResponse(BinaryReader streamReader);

        /// <summary>
        /// 向流中写入字符串
        /// </summary>
        /// <param name="streamWriter"></param>
        /// <param name="str"></param>
        /// <remarks>字符串型数据采用 [长度][ASCII编码] 方式传输</remarks>
        protected static void WriteString(BinaryWriter streamWriter, string str)
        {
            if(str.Length > 255)
            {
                throw new ArgumentOutOfRangeException("str");
            }

            streamWriter.Write((byte)str.Length);
            streamWriter.Write(Encoding.ASCII.GetBytes(str));
        }

        /// <summary>
        /// 从流中读取字符串
        /// </summary>
        /// <param name="streamReader"></param>
        /// <returns></returns>
        /// <remarks>字符串型数据采用 [长度][ASCII编码] 方式传输</remarks>
        protected static string ReadString(BinaryReader streamReader)
        {
            byte len = streamReader.ReadByte();
            byte[] buf = streamReader.ReadBytes(len);
            return Encoding.ASCII.GetString(buf);
        }

        public Guid ID
        {
            get { return _id; }
        }

        public CommandType Type
        {
            get { return _type; }
        }

        /// <summary>
        /// 获取协议的二进度数据
        /// </summary>
        /// <returns></returns>
        public byte[] GetRequest()
        {
            MemoryStream ms = new MemoryStream();
            BinaryWriter bw = new BinaryWriter(ms);

            //写入包头
            //写入ID
            bw.Write(_id.ToByteArray());
            //写入命令类型
            bw.Write((byte)_type);
            //写入包长度占位符
            bw.Write(0x00000000);

            //写入包体
            WriteRequest(bw);

            //重新写入实际长度
            bw.Seek(17, SeekOrigin.Begin);
            bw.Write((uint)ms.Length);

            return ms.ToArray();
        }

        /// <summary>
        /// 解析应答包
        /// </summary>
        /// <param name="dataBuffer"></param>
        public void ParseResponse(byte[] dataBuffer)
        {
            MemoryStream ms = new MemoryStream(dataBuffer);
            BinaryReader br = new BinaryReader(ms);

            //读取包头
            //读入ID
            _id = new Guid(br.ReadBytes(16));
            //读入命令类型
            _type = (CommandType)br.ReadByte();
            //读入包长度
            uint len = br.ReadUInt32();
        }
    }

    /// <summary>
    /// 登录认证
    /// </summary>
    /// <remarks>
    /// 登录请求协议如下：
    ///     PkgHeader(21 bytes)|CustomerID(n bytes)|Validate(n bytes)
    ///     包头                用户ID              验证数据
    ///     －－检验码是对指令字和用户ID的MD5数据
    ///     验证数据由密码的MD5对指定数据内容进行AES加密标识，服务端通过解密后比较原始数据一致
    /// </remarks>
    class LoginProtocol : BaseProtocol
    {
        private string _customerId;
        private string _password;
        private string _watchword;

        public string CustomerID
        {
            get { return _customerId; }
        }

        private byte[] GetWatchword()
        {

        }

        public LoginProtocol(string customerId, string password) : base(CommandType.Login)
        {
            _customerId = customerId;
            _password = password;
            _watchword = "Iliana.Ruler.2011";
        }

        //public LoginProtocol(string customerId, string password, string watchword) : base(CommandType.Login)
        //{
        //    _customerId = customerId;
        //    _password = password;
        //    _watchword = watchword;
        //}

        protected override void WriteRequest(BinaryWriter streamWriter)
        {
            WriteString(streamWriter, _customerId);
        }

        protected override void ReadResponse(BinaryReader streamReader)
        {
        }
    }
}
